大家好,歡迎來到數據新手村的第二十三天!昨天我們掌握了 groupby
這個強大的聚合分析工具,學會了如何從單一表格中萃取高層次的洞見。
但真實世界的數據,很少會整齊地放在一張大表裡。就像我們的 Olist 數據集,客戶資訊、訂單資訊、支付資訊被分別存放在不同的表格中。如果老闆想問:「哪個州的客戶,貢獻了最高的總銷售額?」
要回答這個問題,我們必須先將 customers
表(包含客戶的州資訊)和 payments
表(包含訂單的支付金額)關聯起來。今天,我們就要來學習如何在 Pandas 中,使用 pd.merge()
來完成這個如同數據庫 JOIN
一般的強大操作。
pd.merge()
的核心思想,是找到兩個 DataFrame 之間共同的「鑰匙」欄位,並根據這個欄位的值,將它們的資訊合併在一起。
讓我們用最簡單的 orders
表和 customers
表來舉例。
orders_df
裡有 customer_id
欄位。customers_df
裡也有 customer_id
欄位。customer_id
就是連接這兩張表的「鑰匙」。
import pandas as pd
orders_df = pd.read_csv('../../data/olist_datasets/olist_orders_dataset.csv')
customers_df = pd.read_csv('../../data/olist_datasets/olist_customers_dataset.csv')
# 檢視兩張表的欄位
print("Orders DF Columns:", orders_df.columns)
print("Customers DF Columns:", customers_df.columns)
# 使用 pd.merge() 進行合併
# Pandas 會很聰明地自動找到兩個 DataFrame 中同名的 'customer_id' 作為鑰匙
merged_df = pd.merge(orders_df, customers_df, on='customer_id')
print("\n合併後的 DataFrame (只顯示部分欄位):")
merged_df[['order_id', 'customer_id', 'customer_unique_id', 'customer_city']].head()
輸出結果:
Orders DF Columns: Index(['order_id', 'customer_id', 'order_status', 'order_purchase_timestamp',
'order_approved_at', 'order_delivered_carrier_date',
'order_delivered_customer_date', 'order_estimated_delivery_date',
'delivery_days'],
dtype='object')
Customers DF Columns: Index(['customer_id', 'customer_unique_id', 'customer_zip_code_prefix',
'customer_city', 'customer_state'],
dtype='object')
合併後的 DataFrame (只顯示部分欄位):
order_id | customer_id | customer_unique_id | customer_city | |
---|---|---|---|---|
0 | e481f51cbdc54678b7cc49136f2d6af7 | 9ef432eb6251297304e76186b10a928d | 7c396fd4830fd04220f754e42b4e5bff | sao paulo |
1 | 53cdb2fc8bc7dce0b6741e2150273451 | b0830fb4747a6c6d20dea0b8c802d7ef | af07308b275d755c9edb36a90c618231 | barreiras |
2 | 47770eb9100c2d0c44946d9cf07ec65d | 41ce2a54c0b03bf3443c3d931a367089 | 3a653a41f6f9fc3d2a113cf8398680e8 | vianopolis |
3 | 949d5b44dbf5de918fe9c16f97b45f8a | f88197465ea7920adcdbec7375364d82 | 7c142cf63193a1473d2e66489a9ae977 | sao goncalo do amarante |
4 | ad21c59c0840e6cb83a9ceb5573f8159 | 8ab97904e6daea8866dbdbc4fb7aad2c | 72632f0f9dd73dfee390c9b22eb56dd6 | santo andre |
讓我們建立兩個簡單的 DataFrame 來演示:
df1 = pd.DataFrame({'key': ['A', 'B', 'C', 'D'],
'value1': [1, 2, 3, 4]})
df2 = pd.DataFrame({'key': ['B', 'C', 'D', 'E'],
'value2': [5, 6, 7, 8]})
只保留兩張表中,key 都存在的資料。這是最嚴格的合併。
# 只會保留 key 為 'B', 'C', 'D' 的資料
inner_join_df = pd.merge(df1, df2, on='key', how='inner')
print(inner_join_df)
輸出結果:
key value1 value2
0 B 2 5
1 C 3 6
2 D 4 7
以左邊的 df1 為主,保留 df1 的所有資料。如果在 df2 中找不到對應的 key,value2 欄位會被填上 NaN (缺失值)。
# 會保留 df1 的所有 key ('A', 'B', 'C', 'D')
# 因為 'A' 在 df2 中不存在,所以 value2 的位置會是 NaN
left_join_df = pd.merge(df1, df2, on='key', how='left')
print(left_join_df)
輸出結果:
key value1 value2
0 A 1 NaN
1 B 2 5.0
2 C 3 6.0
3 D 4 7.0
與 left 相反,以右邊的 df2 為主。
# 會保留 df2 的所有 key ('B', 'C', 'D', 'E')
# 因為 'E' 在 df1 中不存在,所以 value1 的位置會是 NaN
right_join_df = pd.merge(df1, df2, on='key', how='right')
print(right_join_df)
輸出結果:
key value1 value2
0 B 2.0 5
1 C 3.0 6
2 D 4.0 7
3 E NaN 8
最寬鬆的合併,保留兩張表中所有出現過的 key。任何一方缺失的值都會被填上 NaN。
# 會保留所有出現過的 key ('A', 'B', 'C', 'D', 'E')
outer_join_df = pd.merge(df1, df2, on='key', how='outer')
print(outer_join_df)
輸出結果:
key value1 value2
0 A 1.0 NaN
1 B 2.0 5.0
2 C 3.0 6.0
3 D 4.0 7.0
4 E NaN 8.0
實戰:回答老闆的問題
現在我們可以回答開頭的問題了:「哪個州的客戶,貢獻了最高的總銷售額?」
# 讀取需要的資料表
orders_df = pd.read_csv('../../data/olist_datasets/olist_orders_dataset.csv')
customers_df = pd.read_csv('../../data/olist_datasets/olist_customers_dataset.csv')
payments_df = pd.read_csv('../../data/olist_datasets/olist_order_payments_dataset.csv')
# 步驟 1: 將訂單與客戶表合併
orders_with_customers = pd.merge(orders_df, customers_df, on='customer_id', how='left')
# 步驟 2: 將合併後的結果,再與支付表合併
full_df = pd.merge(orders_with_customers, payments_df, on='order_id', how='left')
# 步驟 3: 使用 Day 22 學到的 groupby 進行分析
state_total_sales = full_df.groupby('customer_state')['payment_value'].sum()
print("各州總銷售額 (前十名):")
print(state_total_sales.sort_values(ascending=False).head(10))
輸出結果:
各州總銷售額 (前十名):
customer_state
SP 5998226.96
RJ 2144379.69
MG 1872257.26
RS 890898.54
PR 811156.38
SC 623086.43
BA 616645.82
DF 355141.08
GO 350092.31
ES 325967.55
Name: payment_value, dtype: float64
結語
今天我們掌握了 Pandas 中橫向整合資料的強大工具 pd.merge。學會了如何根據共同的「鑰匙」,以及 inner, left, right, outer 四種不同的合併策略,將分散在各處的資訊串連成一張有價值的寬表。
我們的數據準備與處理能力已經越來越完整。但是,Olist 數據集中還有一個非常重要的維度我們尚未深入探索——時間。明天,Day 24,我們將學習 Pandas 強大的時間序列處理能力,分析訂單在不同時間點的變化趨勢!